/* 
 * File:   makefl.cpp
 * Author: Sergey Reymerov
 *
 * Created on 30 Ноябрь 2009 г., 17:00
 * makefl - make full library.
 * Эта программа создает полную библиотеку из найденных ранее.
 * Позволяет находить только библиотеки до 4-х переменных (включительно).
 * Она загружает библиотеку целиком и из нее создает целиком новую библиотеку.
 */
#include "DefineConfiguration.h"
#include "BooleanFunction.h"
#include "FullLibrary.h"
#include "LibraryInterface.h"
#include <iostream>
#include <sstream>
#include <string>
#include "NLLibrary.h"
#include <set>

const UInt32 n = 5;
typedef LibraryInfo<n> CurLibInfo;


class Evo6Library:public LibraryInterface {
public:
    Evo6Library() { }
    ~Evo6Library() { }
    typedef LibraryInfo<5> Lib5;
    enum {
        iterationMax = 15,
        populationMax = 100
    };
    inline void get(const UInt64 f, UInt8 &s) {
        fx01 = f >> 32;
        fx02 = f & 0xFFFFFFFF;
        generateStartPopulation();
        std::vector<std::set<UInt64> > old_populations = populations;
        for(size_t iteration = iterationMax; iteration--; ) {
            for(size_t i = old_populations.size(); i--; ) {
                for(std::set<UInt64>::iterator ii = old_populations[i].begin(); ii != old_populations[i].end(); ii++)
                    for(size_t j = i + 1; j--; ) {
                        for(std::set<UInt64>::iterator jj = old_populations[j].begin(); jj != old_populations[j].end(); jj++) {
                            crossingItems(*ii, *jj);
                        }
                    }
            }            

            for(size_t i = old_populations.size(); i--; )
                for(std::set<UInt64>::iterator ii = old_populations[i].begin(); ii != old_populations[i].end(); ii++)
                    mutateItem(*ii);            
                
            killItems();
        }
        for(size_t i = 0; i < populations.size(); i++) if (populations[i].size() != 0) {
            s = i;
            break;            
        }
        for(size_t i = old_populations.size(); i--; ) populations[i].clear();
    }

    inline void set(const UInt64 f, const UInt8 s) { }
    void load(const std::string filename) {
        nllib.load(filename);
    }
    void save(const std::string filename) { }
    NLLibrary<Lib5> nllib;
    std::vector<std::set<UInt64> > populations;
private:
    UInt64 fx01, fx02;
protected:
    inline void pushToPopulations(const UInt64 f) {
        UInt8 fc = (f & 0xA0000) ^ (f & 0xF000000) ^ (f & 0xF00), fct;
        nllib.get(f, fct); fc = fct;
        nllib.get(f ^ fx01, fct); fc += fct;
        nllib.get(f ^ fx02, fct); fc += fct;
        if (populations.size() <= fc) populations.resize(fc + 1);
        populations[fc].insert(f);
    }
    inline void generateStartPopulation() {
        pushToPopulations(fx01);
        pushToPopulations(fx02);
        pushToPopulations(fx01 ^ fx02);
        pushToPopulations(fx01 | fx02);
        pushToPopulations(fx01 & fx02);
        std::ostringstream filename;
    }
    inline void crossingItems(const UInt64 f1, const UInt64 f2) {
        pushToPopulations((f1 &  AttributeMask<Lib5,0>::value) ^ (f2 & ~AttributeMask<Lib5,0>::value));
        pushToPopulations((f1 & ~AttributeMask<Lib5,0>::value) ^ (f2 &  AttributeMask<Lib5,0>::value));

        pushToPopulations((f1 &  AttributeMask<Lib5,1>::value) ^ (f2 & ~AttributeMask<Lib5,1>::value));
        pushToPopulations((f1 & ~AttributeMask<Lib5,1>::value) ^ (f2 &  AttributeMask<Lib5,1>::value));

        pushToPopulations((f1 &  AttributeMask<Lib5,2>::value) ^ (f2 & ~AttributeMask<Lib5,2>::value));
        pushToPopulations((f1 & ~AttributeMask<Lib5,2>::value) ^ (f2 &  AttributeMask<Lib5,2>::value));

        pushToPopulations((f1 &  AttributeMask<Lib5,3>::value) ^ (f2 & ~AttributeMask<Lib5,3>::value));
        pushToPopulations((f1 & ~AttributeMask<Lib5,3>::value) ^ (f2 &  AttributeMask<Lib5,3>::value));

        pushToPopulations((f1 &  AttributeMask<Lib5,4>::value) ^ (f2 & ~AttributeMask<Lib5,4>::value));
        pushToPopulations((f1 & ~AttributeMask<Lib5,4>::value) ^ (f2 &  AttributeMask<Lib5,4>::value));
        
        for(size_t i = 1; i < 33; i++) 
            pushToPopulations((static_cast<UInt64>(1) << i) - 1);

        pushToPopulations(f1 ^ f2);
    }
    inline void mutateItem(const UInt64 f) {
        UInt64 tf;
        tf = f; BooleanFunction<Lib5>::negation<0>(tf); pushToPopulations(tf);
        tf = f; BooleanFunction<Lib5>::negation<1>(tf); pushToPopulations(tf);
        tf = f; BooleanFunction<Lib5>::negation<2>(tf); pushToPopulations(tf);
        tf = f; BooleanFunction<Lib5>::negation<3>(tf); pushToPopulations(tf);
        tf = f; BooleanFunction<Lib5>::negation<4>(tf); pushToPopulations(tf);
        pushToPopulations(f ^ 0x1);
        pushToPopulations(f ^ 0x2);
        pushToPopulations(f ^ 0x4);
        pushToPopulations(f ^ 0x8);
        pushToPopulations(f ^ 0x10);
        pushToPopulations(f ^ 0x20);
        pushToPopulations(f ^ 0x40);
        pushToPopulations(f ^ 0x80);
        pushToPopulations(f ^ 0x100);
        pushToPopulations(f ^ 0x200);
        pushToPopulations(f ^ 0x400);
        pushToPopulations(f ^ 0x800);
        pushToPopulations(f ^ 0x1000);
        pushToPopulations(f ^ 0x2000);
        pushToPopulations(f ^ 0x4000);
        pushToPopulations(f ^ 0x80000);
        pushToPopulations(f ^ 0x10000);
        pushToPopulations(f ^ 0x20000);
        pushToPopulations(f ^ 0x40000);
        pushToPopulations(f ^ 0x80000);
        pushToPopulations(f ^ 0x800000);
        pushToPopulations(f ^ 0x100000);
        pushToPopulations(f ^ 0x200000);
        pushToPopulations(f ^ 0x400000);
        pushToPopulations(f ^ 0x800000);
        pushToPopulations(f ^ 0x8000000);
        pushToPopulations(f ^ 0x1000000);
        pushToPopulations(f ^ 0x2000000);
        pushToPopulations(f ^ 0x4000000);
        pushToPopulations(f ^ 0x8000000);
        pushToPopulations(f ^ 0x80000000);
        pushToPopulations(f ^ 0x10000000);
        pushToPopulations(f ^ 0x20000000);
        pushToPopulations(f ^ 0x40000000);
        pushToPopulations(f ^ 0x80000000);
    }
    inline void killItems() {
        UInt64 m = populationMax;
        for(size_t i = 0; i < populations.size(); i++) {            
            UInt64 h = (m << 1) / (populations.size() - i);            
            if (h < populations[i].size()) {
                if (h < 2 && populations[i].size() >= 2) h = 2;
                UInt64 toKill = (populations[i].size() - h) >> 1;                
                std::set<UInt64>::iterator j = populations[i].begin();
                for(UInt64 counter = toKill ; counter--; j++) ;
                populations[i].erase(populations[i].begin(), j);

                j = populations[i].end();
                for(UInt64 counter = toKill ; counter--; j--) ;
                populations[i].erase(j, populations[i].end());
                m -= h;
            } else m -= populations[i].size();
        }
        
    }
};

#include <time.h>
#include <stdlib.h>

class Rnd6Library:public LibraryInterface {
public:
    Rnd6Library() { 
        srand(time(NULL));
    }
    ~Rnd6Library() { }
    typedef LibraryInfo<5> Lib5;
    enum {
        numberOfRndF = 100000
    };
    inline void get(const UInt64 f, UInt8 &s) {
        fx01 = f >> 32;
        fx02 = f & 0xFFFFFFFF;
        s = -1;
        UInt8 fc1, fc2, fc3;        
        UInt64 rf3;
        for(UInt64 i = 0; i < numberOfRndF; i++) {
            rf3 = (rand() & 0x7FFFFFFF << 1) ^ (rand() & 1);
            nllib.get(rf3, fc3);
            nllib.get(fx01 ^ rf3, fc1);
            nllib.get(fx02 ^ rf3, fc2);
            if (s > fc1 + fc2 + fc3)
                s = fc1 + fc2 + fc3;
        }
    }

    inline void set(const UInt64 f, const UInt8 s) { }
    void load(const std::string filename) {
        nllib.load(filename);
    }
    void save(const std::string filename) { }
    NLLibrary<Lib5> nllib;
private:
    UInt64 fx01, fx02;
protected:
};


int main() {

    Evo6Library lib;
    //Rnd6Library lib;
    std::ostringstream filename;
    filename << LIBRARYDIRECTORIES << "/nllibs/nllibrary_" << 5 << ".bin";
    lib.load(filename.str());
    std::cout << "Library has loaded..." <<std::endl;
    while(true) {
        UInt64 f;
        std::cin >> std::dec >> f;
        UInt8 tc;
        lib.get(f,tc);
        std::cout << (int) tc << std::endl;
    }


/*    std::ostringstream filename;


    std::ifstream fsfile;
    std::vector<std::vector<UInt64> > fs;
    fs.resize(5);
    for(size_t i = 0; i < 5; i++) {        
        UInt64 size;
        filename << LIBRARYDIRECTORIES << "/fulllibs/fulllibrary_" << (int)n << "_" << i << ".bin";
        fsfile.open(filename.str().c_str(), std::ios::in | std::ios::binary);
        filename.str("");        
        fsfile.seekg(0, std::ios::end);
        size = fsfile.tellg();
        fsfile.seekg(0, std::ios::beg);
        size /= 8;
        fs[i].resize(size);
        std::cout << "Complication " << std::dec << i << " is " << size << std::endl;
        for(size_t j = 0;  j < size; j++)
            fsfile.read((char*)&fs[i][j], sizeof(UInt64));

        fsfile.close();        
    }

    NLLibrary<CurLibInfo> nllib;
    filename << LIBRARYDIRECTORIES << "/nllibs/nllibrary_" << CurLibInfo::n << ".bin";
    nllib.load(filename.str());
    filename.str("");


    UInt64 fsi, f, lpf;

    //std::cin >> std::hex >> lpf >> f;
    lpf = 0x006B6BBD;
    f   = 0x166B7DD6;
    //lpf = 0xDB6DB6DB;
    //f   = 0x6DB6DB6C;

    //const UInt8 fComplication = 14;
    UInt8 ts = 255, ts1, ts2, ts3;
    std::cout << "start minimization" << std::endl;
    for(size_t i = 0; i < 5; i++) {
        for(size_t j = 0; j < fs[i].size(); j++) {
            fsi = fs[i][j];
            nllib.get(fsi ^ lpf, ts1);
            nllib.get(fsi ^ f, ts2);
            if (ts1 + ts2 + i < ts) {
                ts = ts1 + ts2 + i;
            }
            nllib.get(fsi ^ lpf ^ f, ts3);
            if (ts1 + ts3 + i < ts) {
                ts = ts1 + ts3 + i;
            }            
            if (ts2 + ts3 + i < ts) {
                ts = ts2 + ts3 + i;
            }
        }
    }
    std::cout << "end minimization" <<std::endl;
    std::cout << std::dec << (int)ts << std::endl;
    return (0);*/
    
}

